package bittwiddled;

import static bittwiddled.InversionTools.calcNonActivation;
import static bittwiddled.InversionTools.calcNonInhibition;

import java.util.Iterator;
import java.util.NavigableSet;
import java.util.Set;

import bittwiddled.Dynamic.BeforeStates;

import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;

public class Inverse {

	private final BeforeStates dynamic;
				
	static int calcActivation(NavigableSet<Integer> activationTerms, int nonActivation) {
		int activation = 0;
		Iterator<Integer> terms = activationTerms.iterator();
		Set<Integer> replace = Sets.newHashSetWithExpectedSize(activationTerms.size());
		while (terms.hasNext()) {
			int term = terms.next();
			int blend = term & ~nonActivation;
			if (Integer.bitCount(blend) == 1) {
				activation |= blend;
				terms.remove();
			} else if (blend != term) {
				replace.add(blend);
				terms.remove();
			}
		}
		
		if (activation != 0) {
			Predicate<Integer> notSatByActivators = notSatByFixed(activation);		
			Iterables.addAll(activationTerms, Iterables.filter(replace, notSatByActivators));
			reduceTerms(activationTerms, notSatByFixed(activation));
		} else {
			activationTerms.addAll(replace);
			reduceTerms(activationTerms);
		}
		
		return activation;
	}
	
	static Predicate<Integer> notSatByFixed(final int fixed) {
		return new Predicate<Integer>(){
			@Override public boolean apply(Integer input) {
				return (input & fixed) == 0;
			}			
		};
	}
	
	static void reduceTerms(NavigableSet<Integer> terms, Predicate<Integer> notSatByFixed) {
		if (terms.isEmpty()) return;
		Iterator<Integer> nextTerm = terms.iterator();
		int term = 0;

		while (nextTerm.hasNext()) {
			term = nextTerm.next();
			if (notSatByFixed.apply(term)) break;
			else nextTerm.remove();
		}
		
		if (terms.isEmpty()) return;
		int count = Integer.bitCount(term);
		int mark = moreThan(count);
		
		while ( count < Integer.bitCount(terms.last()) ) {
			Iterator<Integer> tail = terms.tailSet(mark).iterator();
			while (tail.hasNext()) {
				int other = tail.next();
				if (!notSatByFixed.apply(other) || (other & term) == term) tail.remove();
			}

			term = terms.higher(term);
			while (!notSatByFixed.apply(term)) {
				terms.remove(term);
				term = terms.higher(term);
			}
			int newcount = Integer.bitCount(term);
			if (count != newcount) {
				mark = shiftMark(mark,newcount-count);
				count = newcount;
			}
		}
	}
	
	static int moreThan(int howMany) {
		int mark = 0;
		for (int i = 0; i<howMany; i++) mark |= Dynamic.bitmask[i];
		return mark;
	}
	
	static int shiftMark(int from, int by) {
		for (int i=0; i< by; i++) from = (from << 1) | 1;
		return from;
	}
	
	static void reduceTerms(NavigableSet<Integer> terms) {
		if (terms.isEmpty()) return;
		
	}
	
	public Inverse(BeforeStates dynamic) {
		this.dynamic = dynamic;
		
		rBar = calcNonInhibition(dynamic.beforeON());
		
		gBar = calcNonActivation(dynamic.beforeOFF(),rBar);
		
		g = calcActivation(dynamic.beforeON(), gBar);
		
		g = 0;
		for (int state : dynamic.beforeON()) {
			int blend = state & ~gBar;
			//if (blend == 0) return junk!
			if (Integer.bitCount(blend) == 1) g |= blend;
		}
		
	}
	
	private int rBar;
	public int nonInhibitors() { return rBar; }
	
	private int gBar;
	public int nonActivators() { return gBar; }
	
	private int g;
	public int activators() { return g; }
	
	private int r;
	public int inhibitors() {
		//TODO implement
		return 0;
	}
	
	private int rstar;
	public int multiplicativeInhibitors() {
		//TODO implement
		return 0;		
	}
	
	private int gstar;
	public int multiplicativeActivators() {
		//TODO implement
		return 0;
	}
	
	public Iterable<EnumeratedSolution> enumerated() {
		//TODO implement
		return null;
	}
	
	public class EnumeratedSolution {
		private final int r;
		private final int g;
		
		EnumeratedSolution(final int inhibitors, final int activators) {
			r = inhibitors;
			g = activators;
		}
		
		public int activators() { return g; }
		public int inhibitors() { return r;	}
	}
	
}
